package room

import (
	"errors"
	"gitee.com/windwolfs/battle/apis/jrpc"
	"gitee.com/windwolfs/battle/client"
	"sync"
)

// Client
// 指客户端的抽象接口
type Client interface {
	SessionId() string

	// ReadPacket 读取客户端消息（包）
	ReadPacket() (*jrpc.Message, error)

	// SendPacket 发送客户端消息包
	SendPacket(message []byte) error

	// Wait 等待客户端关闭
	Wait()

	// Close 关闭客户端
	Close()
}

// Room
// 房间的抽像接口
type Room interface {
	// RoomId 获取房间ID
	RoomId() string

	// Players 获取玩家列表
	Players() []Client

	// Pull 在放那关键内执行等待
	Pull(client Client)

	// Join 加入房间
	Join(client Client)

	// Leave 玩家离开房间
	Leave(client Client)
}

// PullPacket
// 拉取数据包抽象接口
type PullPacket interface {
	// Fetch 拉取游戏数据包
	Fetch(client Client, message *jrpc.Message)
}

// SimpleRoom
// 对房间接口的简单实现
type SimpleRoom struct {
	sync.RWMutex
	PullPacket
	roomId  string
	players map[string]Client
}

// NewSimpleRoom
// 创建基础房间对象
func NewSimpleRoom(roomId string) *SimpleRoom {
	return &SimpleRoom{
		roomId:  roomId,
		players: make(map[string]Client),
	}
}

// RoomId
// 获取房间ID
func (p *SimpleRoom) RoomId() string {
	return p.roomId
}

// Players
// 获取玩家列表
func (p *SimpleRoom) Players() []Client {
	var clients []Client

	for _, cl := range p.players {
		clients = append(clients, cl)
	}

	return clients
}

// Pull
// 在放那关键内执行等待
func (p *SimpleRoom) Pull(cl Client) {
	go func() {
		for {
			message, err := cl.ReadPacket()

			//	如果出错了，则执行关闭客户端操作
			if err != nil {
				//	这一步丢弃数据包
				if errors.Is(err, client.ErrLosePacket) {
					continue
				}

				//	非丢弃连接的数据包
				//	则当关闭轮询对象
				cl.Close()
				return
			}

			if message.PacketType == jrpc.PacketLeave {
				//	非丢弃连接的数据包
				//	则当关闭轮询对象
				cl.Close()
				return
			}

			p.Fetch(cl, message)
		}
	}()

	//	等待轮询结束
	cl.Wait()

	//	当轮询结束时就当执行客户端离开方法
	p.Leave(cl)
}

// Leave
// 玩家离开房间
func (p *SimpleRoom) Leave(client Client) {
	p.Lock()
	defer p.Unlock()

	//	清除房间里的玩家
	delete(p.players, client.SessionId())
}

// Join
// 加入房间
func (p *SimpleRoom) Join(client Client) {
	p.Lock()
	defer p.Unlock()

	p.players[client.SessionId()] = client
}
